<template>
  <div>
    <v-tabs
      @change="onPrevChange"
      v-model="jsonPrevActive"
      class="shrink-0 grow-0"
    >
      <v-tab>模板</v-tab>
      <v-tab>预览</v-tab>
    </v-tabs>
    <div v-show="jsonPrevActive === 0">
      <el-table
        class="json"
        :data="json"
        style="width: 100%;"
        row-key="id"
        :show-header="false"
        default-expand-all
        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      >
        <el-table-column prop="date" label="" sortable width="260">
          <template slot-scope="scope">
            <span class="grow">
              <el-input
                :disabled="fieldDisabled(scope.row)"
                v-model="scope.row.field"
                size="small"
                placeholder="field"
              ></el-input>
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="name" label="" sortable width="180">
          <template slot-scope="scope">
            <span class="flex items-center">
              <el-checkbox
                @change="onChangeRequired(scope.row)"
                :disabled="requiredDisabled(scope.row)"
                class="me-1"
                v-model="scope.row.required"
              ></el-checkbox>
              <el-select
                @change="onChangeType(scope.row)"
                v-model="scope.row.type"
                size="small"
                placeholder="type"
              >
                <el-option
                  v-for="item in typeOptions"
                  :key="item"
                  :label="item"
                  :value="item"
                ></el-option>
              </el-select>
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="name" label="" sortable width="120">
          <template slot-scope="scope">
            <span class="flex items-center">
              <el-select
                v-model="scope.row.mock"
                :disabled="mockDisabled(scope.row)"
                size="small"
                placeholder="mock"
              >
                <el-option
                  v-for="item in mockOptions"
                  :key="item"
                  :label="item"
                  :value="item"
                ></el-option>
              </el-select>
            </span>
          </template>
        </el-table-column>

        <el-table-column prop="date" label="" sortable width="180">
          <template slot-scope="scope">
            <span class="grow flex">
              <el-input
                placeholder="title"
                v-model="scope.row.title"
                size="small"
              ></el-input>
            </span>
          </template>
        </el-table-column>

        <el-table-column prop="date" label="">
          <template slot-scope="scope">
            <span class="grow flex">
              <el-input
                v-model="scope.row.description"
                placeholder="备注"
                size="small"
              ></el-input>
            </span>
          </template>
        </el-table-column>

        <el-table-column prop="date" label="" sortable width="60">
          <template slot-scope="scope">
            <span class="flex items-center">
              <i
                v-show="displayRemove(scope.row)"
                @click="remove(scope.row)"
                class="el-icon-close ms-1 cursor-pointer"
              ></i>
              <i
                v-show="displayPlus(scope.row)"
                @click="createChild(scope.row)"
                class="el-icon-plus ms-1 cursor-pointer"
              ></i>
            </span>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <div v-show="jsonPrevActive === 1">
      <div class="mt-2">
        <!-- <el-input
          type="textarea"
          :rows="16"
          readonly
          v-model="prevText"
        ></el-input> -->
        <v-jsoneditor
          class="res"
          v-model="prevText"
          :options="{ mode: 'code', onEditable: () => {} }"
          readonly
          :plus="true"
          height="400px"
        />
      </div>
    </div>
  </div>
</template>

<script>
import { ref } from "@vue/composition-api";

export default {
  setup() {
    const defaultProps = {
      children: "children",
      label: "label",
    };
    const typeOptions = [
      "object",
      "array",
      "string",
      "number",
      "boolean",
      "integer",
    ];
    const mockOptions = ["@string", "@date"];
    let i = 2;
    const json = ref([
      {
        id: 1,
        field: "root",
        root: true,
        mock: "",
        type: "object",
        title: "",
        description: "",
        required: false,
        children: [],
      },
    ]);

    function parse(obj, parent) {
      const list = [];
      Object.keys(obj).forEach((k) => {
        const dto = obj[k];
        if (["object"].includes(dto.type)) {
          const node = {
            parent,
            id: i++,
            root: false,
            field: k,
            type: dto.type,
            description: dto.description,
            // hasExpand: true,
            children: [],
          };
          node.children = parse(dto.properties, node);
          list.push(node);
        } else if (["array"].includes(dto.type)) {
          const node = {
            parent,
            id: i++,
            root: false,
            field: k,
            type: dto.type,
            description: dto.description,
            // hasExpand: true,
            children: [],
          };
          node.children =
            dto.items.type === "object"
              ? parse(dto.items.properties, node)
              : [{
                  id: i++,
                  items: true,
                  field: "items",
                  type: dto.items.type,
                  children: [],
                }];
          list.push(node);
        } else {
          list.push({
            parent,
            id: i++,
            description: dto.description,
            type: dto.type,
            field: k,
            children: [],
          });
        }
      });
      return list;
    }
    function setBody(val) {
      json.value[0].children = parse(val, json.value[0]);
      console.log(json.value[0].children);
    }

    function unParse(obj) {
      const schema = {
        type: obj.type,
        description: obj.description,
      };
      if (obj.type === "object") {
        schema.properties = {};
        obj.children.forEach((item) => {
          schema.properties[item.field] = unParse(item);
        });
      } else if (obj.type === "array") {
        schema.items = unParse(obj.children[0]);
      }
      return schema;
    }

    function getBody() {
      console.log(json)
      if (["array", "object"].includes(json.value[0].type)) {
        return unParse(json.value[0]);
      } else {
        return {
          type: json.value[0].type,
        };
      }
    }

    function generateLeaf(parent) {
      const leaf = {
        parent,
        id: ++i,
        type: "",
        mock: "",
        field: "",
        title: "",
        description: "",
        required: false,
        children: [],
      };
      return leaf;
    }
    function createChild(parent) {
      console.log(parent);
      const leaf = generateLeaf(parent);
      parent.children.push(leaf);
      changeRequired();
    }
    function remove(ele) {
      console.log(json);
      const index = ele.parent.children.findIndex((item) => item === ele);
      ele.parent.children.splice(index, 1);
      changeRequired();
    }
    function displayPlus(row) {
      if (["object"].includes(row.type)) {
        return true;
      }
    }
    function displayRemove(row) {
      if (row.parent && !row.items) {
        return true;
      }
    }
    function fieldDisabled(row) {
      if (row.root || row.items) {
        return true;
      }
    }
    function mockDisabled(row) {
      if (["object", "array"].includes(row.type)) {
        return true;
      }
    }
    function requiredDisabled(row) {
      if (row.items) {
        return true;
      }
    }
    function onChangeType(row) {
      const { type } = row;
      if ("array" === type) {
        const leaf = generateLeaf(row);
        leaf.items = true;
        leaf.field = "items";
        row.children.push(leaf);
      } else {
        row.children = [];
      }
      if (["object", "array"].includes(row.type)) {
        row.mock = "";
      }
      changeRequired();
    }

    function onChangeRequired(row) {
      if (row.root) {
        if (row.children.length !== 0) {
          changeRootRequired(row);
        }
      } else {
        changeRequired();
      }
    }

    function changeRequired() {
      let required = true;
      const queue = [];
      const root = json.value[0];
      if (root.children.length === 0) {
        return;
      }
      let node = root;
      queue.push(node);
      while (queue.length !== 0) {
        const leaf = queue.shift();
        leaf.children.forEach((child) => {
          if (!child.required && !child.items) {
            required = false;
          }
          queue.push(child);
        });
      }
      root.required = required;
    }

    function changeRootRequired(row) {
      let required = row.required;
      const queue = [];
      const root = json.value[0];
      let node = root;
      queue.push(node);
      while (queue.length !== 0) {
        const leaf = queue.shift();
        leaf.children.forEach((child) => {
          if (!child.items) {
            child.required = required;
          }
          queue.push(child);
        });
      }
    }

    const jsonPrevActive = ref(0);
    const prevText = ref("");
    function parseRoot(root) {
      console.log(root)
      if (root.type === "object") {
        const val = {};
        root.children.forEach((sub) => {
          val[sub.field] = parseRoot(sub);
        });
        return val;
      } else if (root.type === "array") {
        const val = [parseRoot(root.children[0])];
        return val;
      } else if (["number", "integer"].includes(root.type)) {
        return 1;
      } else if ("string" === root.type) {
        return "string";
      } else {
        return "";
      }
    }
    function onPrevChange() {
      console.log(json.value);
      const jsonObj = parseRoot(json.value[0]);
      prevText.value = jsonObj
    }
    return {
      defaultProps,
      json,
      typeOptions,
      mockOptions,
      createChild,
      remove,
      displayPlus,
      displayRemove,
      fieldDisabled,
      mockDisabled,
      requiredDisabled,
      onChangeType,
      onChangeRequired,
      jsonPrevActive,
      prevText,
      onPrevChange,

      setBody,
      getBody,
    };
  },
};
</script>

<style scoped>
.json >>> .cell {
  display: flex;
  align-items: center;
}
</style>